package club.xiaojiawei.dp;

/**
 * @author 肖嘉威
 * @version 1.0
 * @date 5/31/22 8:49 PM
 * @question 509. 斐波那契数
 * @description 斐波那契数 （通常用 F(n) 表示）形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始，后面的每一项数字都是前面两项数字的和。也就是：
 * F(0) = 0，F(1) = 1
 * F(n) = F(n - 1) + F(n - 2)，其中 n > 1
 * 给定 n ，请计算 F(n) 。
 */
public class Fib509 {

    public static void main(String[] args) {
        Fib509 test = new Fib509();
        int result = test.fib(6);
        System.out.println(result);
    }

    /**
     * dp
     * 时间复杂度O(n)
     * 空间复杂度O(n)
     * @param n
     * @return
     */
    public int fib(int n) {
        if (n == 0){
            return 0;
        }else if (n < 3){
            return 1;
        }
//        可以使用两个变量代替这个数组，将空间复杂度将为O(1)
        int[] result = new int[n + 1];
        result[1] = 1;
        for (int i = 2; i <= n; i++) {
            result[i] = result[i - 1] + result[i - 2];
        }
        return result[n];
    }

    /**
     * 官方-矩阵快速幂
     * 时间复杂度O(logn)
     * 空间复杂度O(1)
     * @param n
     * @return
     */
    public int fib2(int n) {
        if (n < 2) {
            return n;
        }
        int[][] q = {{1, 1}, {1, 0}};
        int[][] res = pow(q, n - 1);
        return res[0][0];
    }

    public int[][] pow(int[][] a, int n) {
//        单位矩阵，任何矩阵和单位矩阵相乘都是本身
        int[][] ret = {{1, 0}, {0, 1}};
        while (n > 0) {
            if ((n & 1) == 1) {
                ret = multiply(ret, a);
            }
            n >>= 1;
            a = multiply(a, a);
        }
        return ret;
    }

    public int[][] multiply(int[][] a, int[][] b) {
        int[][] c = new int[2][2];
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j];
            }
        }
        return c;
    }

    /**
     * 通项公式
     * @param n
     * @return
     */
    public int fib3(int n) {
        double sqrt5 = Math.sqrt(5);
        double fibN = Math.pow((1 + sqrt5) / 2, n) - Math.pow((1 - sqrt5) / 2, n);
        return (int) Math.round(fibN / sqrt5);
    }


    static class Test{
        public static void main(String[] args) {
            System.out.println(fastPow(4, 5));
        }

        /**
         * 快速幂
         * @param num 底数
         * @param n 幂
         * @return int
         */
        public static int fastPow(int num, int n){
            int result = 1;
            while (n > 0){
//            如果为奇数
                if ((n & 1) == 1){
                    result *= num;
                }
                num *= num;
                n >>= 1;
            }
            return result;
        }
    }
}
